When `target.$triple.runner` is specified, it will be used for any execution commands by cargo including `cargo run`, `cargo test` and `cargo bench`. The original file is passed to the runner executable as a first argument.
This allows to run tests when cross-comping Rust projects.
This is not a complete solution, and might be extended in future for better ergonomics to support passing extra arguments to the runner itself or overriding runner from command line, but it should already unlock major existing usecases.
Fixes #1411
Resolves #3626
None => return Ok(ret),
};
for (lib_name, value) in table {
- if lib_name == "ar" || lib_name == "linker" || lib_name == "rustflags" {
- continue
+ match lib_name.as_str() {
+ "ar" | "linker" | "runner" | "rustflags" => {
+ continue
+ },
+ _ => {}
}
let mut output = BuildOutput {
use semver::Version;
use core::{PackageId, Package, Target, TargetKind};
-use util::{self, CargoResult, Config, ProcessBuilder, process, join_paths};
+use util::{self, CargoResult, Config, LazyCell, ProcessBuilder, process, join_paths};
/// A structure returning the result of a compilation.
pub struct Compilation<'cfg> {
pub target: String,
config: &'cfg Config,
+
+ target_runner: LazyCell<Option<PathBuf>>,
}
impl<'cfg> Compilation<'cfg> {
cfgs: HashMap::new(),
config: config,
target: String::new(),
+ target_runner: LazyCell::new(),
}
}
self.fill_env(process(cmd), pkg, true)
}
+ fn target_runner(&self) -> CargoResult<&Option<PathBuf>> {
+ self.target_runner.get_or_try_init(|| {
+ let key = format!("target.{}.runner", self.target);
+ Ok(self.config.get_path(&key)?.map(|v| v.val))
+ })
+ }
+
/// See `process`.
pub fn target_process<T: AsRef<OsStr>>(&self, cmd: T, pkg: &Package)
-> CargoResult<ProcessBuilder> {
- self.fill_env(process(cmd), pkg, false)
+ let builder = if let &Some(ref runner) = self.target_runner()? {
+ let mut builder = process(runner);
+ builder.arg(cmd);
+ builder
+ } else {
+ process(cmd)
+ };
+ self.fill_env(builder, pkg, false)
}
/// Prepares a new process with an appropriate environment to run against
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
", url = foo_url, ar = output.0, linker = output.1)))
}
+
+#[test]
+fn custom_runner() {
+ let target = rustc_host();
+
+ let foo = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#)
+ .file("src/main.rs", "fn main() {}")
+ .file("tests/test.rs", "")
+ .file("benches/bench.rs", "")
+ .file(".cargo/config", &format!(r#"
+ [target.{}]
+ runner = "nonexistent-runner"
+ "#, target));
+
+ foo.build();
+
+ assert_that(foo.cargo("run").args(&["--", "--param"]),
+ execs().with_stderr_contains(&format!("\
+[COMPILING] foo v0.0.1 ({url})
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] `nonexistent-runner target[/]debug[/]foo[EXE] --param`
+", url = foo.url())));
+
+ assert_that(foo.cargo("test").args(&["--test", "test", "--verbose", "--", "--param"]),
+ execs().with_stderr_contains(&format!("\
+[COMPILING] foo v0.0.1 ({url})
+[RUNNING] `rustc [..]`
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] `nonexistent-runner [..][/]target[/]debug[/]deps[/]test-[..][EXE] --param`
+", url = foo.url())));
+
+ assert_that(foo.cargo("bench").args(&["--bench", "bench", "--verbose", "--", "--param"]),
+ execs().with_stderr_contains(&format!("\
+[COMPILING] foo v0.0.1 ({url})
+[RUNNING] `rustc [..]`
+[RUNNING] `rustc [..]`
+[FINISHED] release [optimized] target(s) in [..]
+[RUNNING] `nonexistent-runner [..][/]target[/]release[/]deps[/]bench-[..][EXE] --param --bench`
+", url = foo.url())));
+}